package control

import (
	"hi-jvm/instructions/base"
	"hi-jvm/rtda"
)

// Return void from method
type RETURN struct{ base.NoOperandsInstruction }

func (self *RETURN) Execute(frame *rtda.Frame) {
	frame.Thread.PopFrame()
}

// Return reference from method
type ARETURN struct{ base.NoOperandsInstruction }

func (self *ARETURN) Execute(frame *rtda.Frame) {
	return_execute(frame, func(currentFrame *rtda.Frame, invokerFrame *rtda.Frame){
		ref := currentFrame.OperandStack.PopRef()
		invokerFrame.OperandStack.PushRef(ref)
	})
}

// Return double from method
type DRETURN struct{ base.NoOperandsInstruction }

func (self *DRETURN) Execute(frame *rtda.Frame) {
	return_execute(frame, func(currentFrame *rtda.Frame, invokerFrame *rtda.Frame){
		val := currentFrame.OperandStack.PopDouble()
		invokerFrame.OperandStack.PushDouble(val)
	})
}

// Return float from method
type FRETURN struct{ base.NoOperandsInstruction }

func (self *FRETURN) Execute(frame *rtda.Frame) {
	return_execute(frame, func(currentFrame *rtda.Frame, invokerFrame *rtda.Frame){
		val := currentFrame.OperandStack.PopFloat()
		invokerFrame.OperandStack.PushFloat(val)
	})
}

// Return int from method
type IRETURN struct{ base.NoOperandsInstruction }

func (self *IRETURN) Execute(frame *rtda.Frame) {
	return_execute(frame, func(currentFrame *rtda.Frame, invokerFrame *rtda.Frame){
		val := currentFrame.OperandStack.PopInt()
		invokerFrame.OperandStack.PushInt(val)
	})
}

// Return double from method
type LRETURN struct{ base.NoOperandsInstruction }

func (self *LRETURN) Execute(frame *rtda.Frame) {
	return_execute(frame, func(currentFrame *rtda.Frame, invokerFrame *rtda.Frame){
		val := currentFrame.OperandStack.PopLong()
		invokerFrame.OperandStack.PushLong(val)
	})
}

func return_execute(frame *rtda.Frame, callback func(*rtda.Frame, *rtda.Frame)) {
	thread := frame.Thread
	currentFrame := thread.PopFrame()
	invokerFrame := thread.TopFrame()
	callback(currentFrame,  invokerFrame)
}

